home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Diamond Collection / The Diamond Collection (Software Vault)(Digital Impact).ISO / cdr40 / radserv.zip / TTYORIG.C < prev    next >
C/C++ Source or Header  |  1994-11-14  |  15KB  |  530 lines

  1. /* %W% %E% %U% */
  2. /*
  3.  * Sample Windows-NT TTY program. Runs as an NT Console Application
  4.  * and passes keyboard data to TTY and writes TTY chars to console.
  5.  * S.N. 11/2/94.
  6.  * Compile with Microsoft WIN32 compiler:
  7.  *   cl -O1 -Os -DWIN32 tty.c -link -defaultlib:user32,gdi32,advapi32
  8.  */
  9.  
  10. #include <windows.h>
  11. #include <stdlib.h>
  12. #include <stdio.h>
  13.  
  14. /*
  15.  * Flags
  16.  */
  17. #define TTY_RTSCTS  0x1     /* RTS/CTS Handshake */
  18. #define TTY_DSRDTR  0x2     /* DSR/DTR Handshake */
  19. #define TTY_XONXOFF 0x4     /* XON-XOFF Handshake by COM port driver */
  20.  
  21. typedef struct tag_ttyinfo {
  22.     int         t_portno;
  23.     int         t_baudrate;
  24.     char        t_databits;
  25.     char        t_parity;
  26.     char        t_stopbits;
  27.     int         t_flags;
  28.     HANDLE      t_ttyh;
  29.     HANDLE      t_WrEvent;
  30.     HANDLE      t_RdEvent;
  31.     OVERLAPPED  t_WrOverlap;
  32.     OVERLAPPED  t_RdOverlap;
  33. } tty_t;
  34.  
  35. struct ports {
  36.     char *str;
  37.     int  code;
  38. } Ports[] = {
  39.     "COM1", 1, "COM2", 2, "COM3", 3, "COM4", 4, /* etc. etc. etc. */ 0 ,0
  40. };
  41.  
  42. struct bauds {
  43.     char *str;
  44.     int  code;
  45. } Bauds[] = {
  46.     "300", CBR_300, "600", CBR_600, "1200", CBR_1200,
  47.     "2400", CBR_2400, "4800", CBR_4800, "9600", CBR_9600,
  48.     "14400", CBR_14400, "19200", CBR_19200, 0, 0
  49. };
  50.  
  51. struct stopbits {
  52.     char *str;
  53.     int code;
  54. } Stopbits[] = {
  55.     "1", ONESTOPBIT, "1.5", ONE5STOPBITS, "2", TWOSTOPBITS, 0, 0
  56. };
  57.  
  58. struct dbits {
  59.     char *str;
  60.     int code;
  61. } DataBits[] = {
  62.     "5", 5, "6", 6, "7", 7, "8", 8, 0, 0
  63. };
  64.  
  65. struct parity {
  66.     char *str;
  67.     int code;
  68. } Parity[] = {
  69.     "None", NOPARITY , "Odd", ODDPARITY, "Even", EVENPARITY,
  70.     "AlwaysMark", MARKPARITY, "AlwaysSpace", SPACEPARITY, 0, 0
  71. };
  72.  
  73.  
  74. BOOL
  75. OpenLine ( tty_t *tp )
  76. {
  77.     char       portname[24];
  78.     BOOL       result;
  79.     COMMTIMEOUTS  CommTimeOuts ;
  80.     extern BOOL TTYSetup(tty_t *);
  81.  
  82.     sprintf(portname, "\\\\.\\COM%d", tp->t_portno) ;
  83.  
  84.     tp->t_ttyh = CreateFile( portname, GENERIC_READ | GENERIC_WRITE,
  85.                   0 /* Exclusive Access */,
  86.                   NULL,
  87.                   OPEN_EXISTING,  FILE_ATTRIBUTE_NORMAL | 
  88.                   FILE_FLAG_OVERLAPPED, NULL);
  89.                   
  90.     if (tp->t_ttyh == INVALID_HANDLE_VALUE) {
  91.         printf("Cannot open COM%d - error %d\n", tp->t_portno, GetLastError());
  92.         return FALSE;
  93.     }
  94.  
  95.     CommTimeOuts.ReadIntervalTimeout = 0xffffffff ; /* Indefinite */
  96.     CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
  97.     CommTimeOuts.ReadTotalTimeoutConstant = 0;
  98.     CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
  99.     CommTimeOuts.WriteTotalTimeoutConstant = 0xffffffff; /* indefinite */
  100.    
  101.     SetCommTimeouts(tp->t_ttyh, &CommTimeOuts);
  102.  
  103.     result = TTYSetup(tp) ;
  104.  
  105.     if (!result) {
  106.        CloseHandle(tp->t_ttyh) ;
  107.        return FALSE;
  108.     }
  109.     SetCommMask(tp->t_ttyh, EV_RING | EV_RXCHAR | EV_DSR | EV_RLSD | EV_BREAK) ;
  110.  
  111.     SetupComm(tp->t_ttyh, 4096, 4096) ;
  112.     
  113.     if ((tp->t_RdEvent = CreateEvent(NULL,TRUE,FALSE,NULL)) == INVALID_HANDLE_VALUE) {
  114.         CloseHandle(tp->t_ttyh);
  115.         return FALSE;
  116.     }    
  117.     memset (&tp->t_RdOverlap, 0, sizeof(OVERLAPPED));
  118.     tp->t_RdOverlap.hEvent = tp->t_RdEvent;
  119.  
  120.     if ((tp->t_WrEvent = CreateEvent(NULL,TRUE,FALSE,NULL)) == INVALID_HANDLE_VALUE) {
  121.         CloseHandle(tp->t_ttyh);
  122.         return FALSE;
  123.     }    
  124.     memset (&tp->t_WrOverlap, 0, sizeof(OVERLAPPED));
  125.     tp->t_WrOverlap.hEvent = tp->t_WrEvent;
  126.     
  127.     /*
  128.      * If no hardware handshaking desired, raise DTR and RTS
  129.      */
  130.  
  131.     if (!(tp->t_flags & TTY_DSRDTR))
  132.         EscapeCommFunction( tp->t_ttyh, SETDTR);
  133.         
  134.     Sleep(100);
  135.     
  136.     if (!(tp->t_flags & TTY_RTSCTS))
  137.         EscapeCommFunction( tp->t_ttyh, SETRTS);
  138.     
  139.     return TRUE;
  140. }
  141.  
  142.  
  143. static BOOL
  144. TTYSetup ( tty_t *tp )
  145. {
  146.     DCB        dcb ;
  147.     
  148.     dcb.DCBlength = sizeof(DCB);
  149.     GetCommState(tp->t_ttyh, &dcb) ;
  150.     
  151.     dcb.BaudRate = tp->t_baudrate ;
  152.     dcb.ByteSize = tp->t_databits ;
  153.     dcb.Parity = tp->t_parity ;
  154.     dcb.StopBits = tp->t_stopbits ;
  155.  
  156.     if (dcb.fOutxDsrFlow = ((tp->t_flags & TTY_DSRDTR) != 0))
  157.        dcb.fDtrControl = DTR_CONTROL_HANDSHAKE ;
  158.     else
  159.        dcb.fDtrControl = DTR_CONTROL_ENABLE ;
  160.        
  161.     dcb.fDsrSensitivity = 0;
  162.     
  163.     if (dcb.fOutxCtsFlow = ((tp->t_flags & TTY_RTSCTS) != 0)) 
  164.        dcb.fRtsControl = RTS_CONTROL_HANDSHAKE ;
  165.     else
  166.        dcb.fRtsControl = RTS_CONTROL_ENABLE ;
  167.  
  168.     dcb.fInX = dcb.fOutX = (BYTE) ((tp->t_flags & TTY_XONXOFF) != 0);
  169.     dcb.fTXContinueOnXoff = TRUE;
  170.     dcb.fNull = TRUE;
  171.     dcb.fAbortOnError = FALSE;
  172.     dcb.XonChar = (char )17;   /* ASCII XON */
  173.     dcb.XoffChar = (char )19;  /* ASCII XOFF */
  174.     dcb.XonLim = 100;
  175.     dcb.XoffLim = 100;
  176.  
  177.     dcb.fBinary = TRUE ;
  178.     dcb.fParity = (tp->t_parity != NOPARITY);
  179.  
  180.     return SetCommState( tp->t_ttyh, &dcb ) ;
  181. }
  182.  
  183. static BOOL
  184. CloseLine(tty_t *tp)
  185. {
  186.     BOOL status;
  187.     
  188.     /* Stop receiving event notifications */
  189.     
  190.     SetCommMask( tp->t_ttyh, 0);
  191.  
  192.     /* Drop RTS */
  193.     EscapeCommFunction( tp->t_ttyh, CLRRTS);
  194.     Sleep(100);
  195.  
  196.     /* Drop DTR */
  197.     EscapeCommFunction( tp->t_ttyh, CLRDTR);
  198.     Sleep(100);
  199.     
  200.     /* Purge all data in all buffers */
  201.  
  202.     PurgeComm( tp->t_ttyh, PURGE_TXABORT | PURGE_RXABORT |
  203.                            PURGE_TXCLEAR | PURGE_RXCLEAR );
  204.  
  205.     /* Release resources */
  206.     
  207.     if (status = CloseHandle(tp->t_ttyh)) {
  208.         tp->t_ttyh = NULL;
  209.         CloseHandle(tp->t_RdEvent);
  210.         DeleteObject(tp->t_RdEvent);
  211.         CloseHandle(tp->t_WrEvent);
  212.         DeleteObject(tp->t_WrEvent);
  213.     }
  214.     
  215.     return status;
  216. }
  217.  
  218. /*
  219.  * Read up to max nMaxLength chars into lpszBlock.
  220.    Returns:  0: Nothing available (nothing received)
  221.             >0: No. of chars received (and put in lpszBlock)
  222.             -1: Error occured. 'error' contains code (either OS error
  223.                 code or driver-specific code)
  224. */
  225.  
  226. static int
  227. ReadCommBuf(tty_t *tp, LPSTR lpszBlock, int nMaxLength, int *error )
  228. {
  229.     BOOL    status;
  230.     COMSTAT ComStat;
  231.     DWORD   dwErrorFlags, dwLength;
  232.     int     lerror;
  233.  
  234.     *error = 0;
  235.     ClearCommError(tp->t_ttyh, &dwErrorFlags, &ComStat ) ;
  236.  
  237.     if (dwErrorFlags > 0) {
  238.        *error = (int) dwErrorFlags;
  239.        return -1;
  240.     }
  241.  
  242.     dwLength = min((int)nMaxLength, (int)ComStat.cbInQue);
  243.  
  244.     if (dwLength > 0) {
  245.        status = ReadFile( tp->t_ttyh, lpszBlock,
  246.                              dwLength, &dwLength, &tp->t_RdOverlap) ;
  247.        if (!status) {
  248.           if ((lerror = GetLastError()) == ERROR_IO_PENDING) {
  249.              if (WaitForSingleObject( tp->t_RdOverlap.hEvent, 20000)) {
  250.                 *error = GetLastError();
  251.                 return -1;
  252.              }
  253.              else {
  254.                 GetOverlappedResult( tp->t_ttyh, &tp->t_RdOverlap, &dwLength, FALSE ) ;
  255.                 tp->t_RdOverlap.Offset += dwLength;
  256.              }
  257.           }
  258.           else {
  259.             printf("\n<Line error %08x>\n", lerror);
  260.             *error = lerror;
  261.             return -1;
  262.           }
  263.        }
  264.     }
  265.     return dwLength;
  266. }
  267.  
  268. /* Write a character to tty port. If this routine is called at more than
  269.    human typing speeds, the stuff inside the ifdef should be included in
  270.    the compile.
  271. */
  272.  
  273. static void
  274. WriteCommByte(tty_t *tp, BYTE bByte)
  275. {
  276.     int     status, error;
  277.     DWORD   BytesWritten;
  278.  
  279.     status = WriteFile( tp->t_ttyh, (LPSTR) &bByte, 1,
  280.                             &BytesWritten, &tp->t_WrOverlap);
  281.  
  282.     if (!status) {
  283.         if ((error = GetLastError()) == ERROR_IO_PENDING) {
  284.             status = WaitForSingleObject( tp->t_WrOverlap.hEvent, 10000);
  285.             if ( status == WAIT_OBJECT_0 ) {
  286.                 GetOverlappedResult( tp->t_ttyh, &tp->t_WrOverlap, &BytesWritten, FALSE );
  287.                 tp->t_WrOverlap.Offset += BytesWritten;
  288.             }
  289.             else {
  290.                 /* Other Error occured (including a timeout). */ ;
  291.             }
  292.         }
  293.         else {
  294.             /* Other I/O error occured. */ ;
  295.         }
  296.     }
  297. }
  298.  
  299. /*
  300.  * Write an array of characters to tty port */
  301.  
  302. static void
  303. WriteCommBuf(tty_t *tp, char *bufp, int len)
  304. {
  305.     for (; len > 0; len--)
  306.         WriteCommByte(tp, *bufp++);
  307. }
  308.  
  309. /*
  310.  * Open tty line. all parameters are specified in strings (i.e.,
  311.   "COM2", "9600", etc. No provision to set the hardware or software
  312.   handshaking by setting the appropriate flags, etc.
  313. */
  314.  
  315. BOOL
  316. TTYOpen(tty_t *tp, char * portp, char *speedp, char *databitsp, char *stopbitsp,
  317.     char *parityp)
  318. {
  319.     struct ports *pp;
  320.     struct bauds *bp;
  321.     struct dbits *dbitsp;
  322.     struct stopbits *sp;
  323.     struct parity *parp;
  324.  
  325.     tp->t_ttyh = NULL;
  326.  
  327.     if (!portp || !speedp || !parityp || !databitsp || !stopbitsp)
  328.         return FALSE;
  329.         
  330.     for (pp = Ports; pp->str; pp++)
  331.         if (strcmp(pp->str, portp) == 0)
  332.             tp->t_portno = pp->code;
  333.  
  334.     for (bp = Bauds; bp->str; bp++)
  335.         if (strcmp(bp->str, speedp) == 0)
  336.             tp->t_baudrate = bp->code;
  337.  
  338.     for (dbitsp = DataBits; dbitsp->str; dbitsp++)
  339.         if (strcmp(dbitsp->str, databitsp) == 0)
  340.             tp->t_databits = dbitsp->code;
  341.  
  342.     for (sp = Stopbits; sp->str; sp++)
  343.         if (strcmp(sp->str, stopbitsp) == 0)
  344.             tp->t_stopbits = sp->code;
  345.  
  346.     for (parp = Parity; parp->str; parp++)
  347.         if (strcmp(parp->str, parityp) == 0)
  348.             tp->t_parity = parp->code;
  349.             
  350.     return OpenLine(tp);
  351. }    
  352.  
  353. /*
  354.  * Default parameters for the port
  355.  */
  356.  
  357. tty_t tty = { 2, CBR_19200, 8, NOPARITY, ONESTOPBIT, 0,
  358.     NULL, NULL, NULL, { 0 }, { 0 }
  359. };
  360.  
  361.  
  362. HANDLE hConOutput, thread;
  363.  
  364. void /* !!! */
  365. main(ac, av)
  366. char *av[];
  367. {
  368.     HANDLE hConInput;
  369.     DWORD dummy, mode, oldmode;
  370.     char c;
  371.     int rd;
  372.     extern void Reader(tty_t *);
  373.  
  374.     /*
  375.      * Change params as needed: Portname, speed, databits, stopbits, parity.
  376.      * Note: not all data and stopbit combinations are supported by the
  377.      * driver.
  378.      */
  379.  
  380.     if (!TTYOpen(&tty, "COM2", "14400", "8", "1", "None")) {
  381.         printf("can't open/set up port COM2");
  382.         exit(0);
  383.     }
  384.     
  385.     /*
  386.      * Set Keyboard input to uncooked mode (no line delimiter, no echo)
  387.      */
  388.          
  389.     hConInput = GetStdHandle(STD_INPUT_HANDLE);
  390.     hConOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  391.  
  392.     if (!hConInput || !hConOutput) {
  393.         printf("can't get stdin/stdout handle\n");
  394.         exit(1);
  395.     }
  396.  
  397.     if (GetConsoleMode(hConInput, &oldmode) == FALSE) {
  398.         printf("can't get console mode\n");
  399.         exit(1);
  400.     }
  401.  
  402.     mode = oldmode; /* Keep a copy for later restoration */
  403.     mode &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
  404.  
  405.     if (SetConsoleMode(hConInput, mode) == FALSE) {
  406.         printf("can't set console mode\n");
  407.         exit(1);
  408.     }
  409.     
  410.     /*
  411.      * Create auxiliary thread to read the COM port and write to screen
  412.      */
  413.  
  414.     thread = CreateThread ((LPSECURITY_ATTRIBUTES)NULL,  (DWORD)0,
  415.         (LPTHREAD_START_ROUTINE)Reader, (LPVOID)&tty,
  416.         (DWORD)0, (LPDWORD)&dummy);
  417.  
  418.     if (thread == INVALID_HANDLE_VALUE) {
  419.         printf("can't create reader thread\n");
  420.         exit(1);
  421.     }
  422.  
  423.     /*
  424.      * Begin main thread. Read keyboard and write to the COM port.
  425.      */
  426.      
  427.     for(;;) {
  428.  
  429.         if (ReadFile(hConInput, &c, 1, &rd, NULL) == FALSE)
  430.             break;
  431.  
  432.         if (rd > 0)
  433.             WriteCommByte(&tty, c);
  434.  
  435.         if (!thread) { /* Beware of compiler optimizations */
  436.             printf("\nOops! Reader went away. Exiting...\n");
  437.             break;
  438.         }
  439.     }
  440.     
  441.     if (thread)
  442.         (void) TerminateThread(thread /* bye-bye */, 0);
  443.     
  444.     if (CloseLine(&tty))
  445.         printf("Line Closed\n");
  446.     else
  447.         printf("error closing com port\n");
  448.         
  449.     (void) SetConsoleMode(hConInput, oldmode); /* if we can't set , tuff */
  450.     
  451.     exit(0);
  452. }    
  453.  
  454. static void
  455. Reader(tty_t *tp)
  456. {
  457.     int rd, error, nwr, status;
  458.     int EventMask;
  459.     char buf[256];
  460.     
  461.     printf("\nDumb Terminal Emulator Program\n");
  462.     printf("****       IGOR LABS        ****\n");
  463.     printf("**** Falls Church, VA 22042 ****\n");
  464.     printf("\nCOM2  Baudrate:14400  8bits  1Stopbit  NoParity\n");
  465.     printf("\nHit Control-C To Exit (YOU CAN'T PASS ^C TO REMOTE)\n");
  466.     fflush(stdout);
  467.     
  468.     for (;;) {
  469.         EventMask = 0;
  470.         
  471.         /* Wait for 10 seconds for something to happen */
  472.         
  473. #ifdef WEWANTTOBEASCOMPLICATEDASPOSSIBLE
  474.  
  475.         status = WaitCommEvent(tp->t_ttyh, &EventMask, &tp->t_RdOverlap);
  476.  
  477.         if (!status) {
  478.             error = GetLastError();
  479.             if ((unsigned)error == ERROR_IO_PENDING)
  480.                 WaitForSingleObject(tp->t_RdEvent, 10000);
  481.             else {
  482.                 printf("\n<Error (%d)>\n", error);
  483.                 break;
  484.             }
  485.         }
  486. #else
  487.         /*
  488.          * Wait indefinitely (barring a ^C) for something to
  489.          * happen on the COM port.
  490.          */
  491.          
  492.         (void) WaitCommEvent(tp->t_ttyh, &EventMask, NULL);
  493. #endif
  494.         
  495.         if (EventMask & (EV_DSR| EV_RLSD | EV_BREAK | EV_RING)) {
  496.             GetCommModemStatus(tp->t_ttyh, &status);
  497.             if (EventMask & EV_DSR)
  498.                 printf("\n<DSR %sASSERTED>\n", (status & MS_DSR_ON) ? "" : "DE");
  499.             if (EventMask & EV_RLSD);
  500.                 printf("\n<CARRIER DETECT %sASSERTED>\n", (status & MS_RLSD_ON) ? "" : "DE");
  501.             if (EventMask & EV_BREAK)
  502.                 printf("\n<LINE BREAK>\n");
  503.             if ((EventMask & EV_RING) && (status & MS_RING_ON))
  504.                 printf("\n<RRRRING!>\n");
  505.         }
  506.         
  507.         /* Now attend to the business of reading the port and echoing */
  508.         
  509.         if (EventMask & EV_RXCHAR) {
  510.             do {
  511.                 rd = ReadCommBuf(tp, buf, sizeof buf, &error);
  512.                 if (rd > 0)
  513.                     WriteFile(hConOutput, buf, rd, &nwr, NULL);
  514.             } while(rd > 0);
  515.         }
  516.     }
  517.     
  518.     /*
  519.      * If "wrong" level of optimization is used when compiling, the
  520.      * main thread may not be able to discover that the value of the
  521.      * handle "thread" (below) has changed (i.e., variable aliasing.)
  522.      * This can be avoided by resetting the console mode and
  523.      * TerminateProcess(). For now, we just go ahead with this method.
  524.      */
  525.  
  526.     thread = NULL; /* Signal main thread we're exiting */
  527.  
  528.     ExitThread(0);
  529. }
  530.